#include "Transform.h"
#include "Point.h"

namespace MathGraphics
{
	Transform translate(const Vector& delta)
	{
		Matrix m = Matrix(1.0f, 0.0f, 0.0f, delta.x,
			              0.0f, 1.0f, 0.0f, delta.y,
						  0.0f, 0.0f, 1.0f, delta.z,
						  0.0f, 0.0f, 0.0f, 1.0f);

		Matrix invt = Matrix(1.0f, 0.0f, 0.0f, -delta.x,
			                 0.0f, 1.0f, 0.0f, -delta.y,
						     0.0f, 0.0f, 1.0f, -delta.z,
						     0.0f, 0.0f, 0.0f, 1.0f);

		return Transform(m, invt);
	}

	Transform scale(double x, double y, double z)
	{
		Matrix m = Matrix(x, 0, 0, 0,
						  0, y, 0, 0,
						  0, 0, z, 0,
						  0, 0, 0, 1);

		Matrix invt = Matrix(1/x, 0,   0,   0,
							 0,   1/y, 0,   0,
							 0,   0,   1/z, 0,
							 0,   0,   0,   1);

		return Transform(m, invt);
	}

	Transform rotateX(double angle)
	{
		Matrix m = Matrix(1.0, 0.0,         0.0,         0.0,
						  0.0, cos(angle),  sin(angle),  0.0,
						  0.0, -sin(angle), cos(angle),  0.0,
						  0.0, 0.0,         0.0,         1.0);

		return Transform(m);
	}

	Transform rotateY(double angle)
 	{
		Matrix m = Matrix( cos(angle), 0.0, -sin(angle), 0.0,
						   0.0,        1.0, 0.0,         0.0,
						   sin(angle), 0.0, cos(angle),  0.0,
						   0.0,        0.0, 0.0,         1.0);

		return Transform(m);
	}

	Transform rotateZ(double angle)
	{
		Matrix m = Matrix(-cos(angle), sin(angle), 0.0, 0.0,
			              sin(angle),  cos(angle), 0.0, 0.0,
						  0.0,         0.0,        1.0, 0.0,
						  0.0,         0.0,        0.0, 1.0);

		return Transform(m);
	}

	Transform lookAt(const Point& pos, const Point& look, const Vector& up)
	{
		Vector dir   = (look - pos).normalize();
		Vector right = crossProduct(up, dir).normalize();
		Vector nup   = crossProduct(right, dir);

		Matrix cameraToWorld = Matrix(right.x, nup.x, dir.x, pos.x,
			                          right.y, nup.y, dir.y, pos.y,
						              right.z, nup.z, dir.z, pos.z,
						              0.0,     0.0,   0.0,   1.0);

		return Transform(cameraToWorld.inverse(), cameraToWorld);
	}

	Transform orthographic(double near, double far)
	{
		return scale(1.0, 1.0, 1.0 / (far - near)) * translate(Vector(0.0, 0.0, -near));
	}

}